package com.sunnada.weixin.util.weixinPay;

import java.io.ByteArrayOutputStream;
import java.math.BigInteger;
import java.nio.charset.Charset;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.security.spec.X509EncodedKeySpec;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;
import org.junit.Test;

import com.sunnada.weixin.util.weixinPay.ConvertionUtil;

public final class SecurityUtil {

	private static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
	private static final String ALGORITHM_RSA = "RSA";

	private static final int RADIX = 16;
	public static final String PUBLIC_KEY_BASE64 = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDBmvwia5t8kgy7zuLFlZRIfzI48fLMFl9253/ukVv2zGkU2R8K+tzumnFSXcHWMshpWC1WpaTIK91W96rxTe/mA4JSv+9vMyYvYO4xv2bs4n6oJb+X2ZQ9p9tYlSzwLQKEvCk6de58a7f5TX+XwEQxGTgItrAVdrgq3Ndwxuc7BQIDAQAB";
	public static final String MODULUS_PUBLIC_KEY = "c19afc226b9b7c920cbbcee2c59594487f3238f1f2cc165f76e77fee915bf6cc6914d91f0afadcee9a71525dc1d632c869582d56a5a4c82bdd56f7aaf14defe6038252bfef6f33262f60ee31bf66ece27ea825bf97d9943da7db58952cf02d0284bc293a75ee7c6bb7f94d7f97c04431193808b6b01576b82adcd770c6e73b05";
	public static final String EXPONENT_PUBLIC_KEY = "10001";
	public static final String MODULUS_PRIVATE_KEY = "3132bf4bc7e895394fff11cdd2c3f6108e32270264deb3f8c27085b40c404071256ae4d9dca26f2e3b14fd075250aa4aff6a26eb53e091f583a3ac00469db58e5eacd6a94416bab8c4e998ea0d88ec4e3f15efba08f1b4f0d04f89f17e210c93144093d02abec34271066e77812f287357c2a67814c9bb878def3f094845afe9";

	private static PrivateKey sDefPrivateKey = buildDefPrivateKey();
	private static PublicKey sDefPublicKey = buildDefPublicKey();

	/**
	 * 生成密钥对
	 */
	@Test
	public void genkeyPairStr() throws NoSuchAlgorithmException {
		KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM_RSA);
		keyPairGenerator.initialize(1024, new SecureRandom());

		KeyPair keyPair = keyPairGenerator.generateKeyPair();
		RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
		RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();

		System.out.println("公钥：");
		System.out.println(publicKey.getModulus().toString(RADIX));
		System.out.println(publicKey.getPublicExponent().toString(RADIX));
		System.out.println("私钥：");
		System.out.println(privateKey.getModulus().toString(RADIX));
		System.out.println(privateKey.getPrivateExponent().toString(RADIX));
	}

	private static PublicKey buildDefPublicKey() {
		try {
			return KeyFactory.getInstance(ALGORITHM_RSA).generatePublic(new RSAPublicKeySpec(
					new BigInteger(MODULUS_PUBLIC_KEY, RADIX), new BigInteger(EXPONENT_PUBLIC_KEY, RADIX)));
		} catch (InvalidKeySpecException e) {
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		return null;
	}

	private static PrivateKey buildDefPrivateKey() {
		try {
			KeySpec keySpec = new RSAPrivateKeySpec(new BigInteger(MODULUS_PUBLIC_KEY, RADIX),
					new BigInteger(MODULUS_PRIVATE_KEY, RADIX));
			KeyFactory factory = KeyFactory.getInstance(ALGORITHM_RSA);
			PrivateKey privateKey = factory.generatePrivate(keySpec);
			return privateKey;
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (InvalidKeySpecException e) {
			e.printStackTrace();
		}
		return null;
	}

	public static Cipher getRsaCipher() throws NoSuchAlgorithmException, NoSuchPaddingException {
		return Cipher.getInstance("RSA/ECB/PKCS1Padding", new org.bouncycastle.jce.provider.BouncyCastleProvider());
	}

	public static Cipher getAesCipher() throws NoSuchAlgorithmException, NoSuchPaddingException {
		return Cipher.getInstance("AES/ECB/PKCS7Padding", new org.bouncycastle.jce.provider.BouncyCastleProvider());
	}

	// RSAEncrypt
	public static String RSAEncrypt(String rawData) throws Exception {
		return ConvertionUtil.BytesToHexString(RSAEncrypt(ConvertionUtil.StringToUTF8BytesPadding(rawData)), true);
	}

	public static byte[] rsaEncrypt2Base64(String rawData) {
		return Base64.encodeBase64(RSAEncrypt(rawData.getBytes()));
	}

	public static byte[] RSAEncrypt(byte[] rawData) {
		return RSAEncrypt(sDefPublicKey, rawData);
	}

//	// 后添加
//	public static String RSAEncrypt(String key, String rawData)
//			throws NoSuchAlgorithmException, InvalidKeySpecException {
//		byte[] publicBytes = Base64.decodeBase64(key);
//		X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicBytes);
//		KeyFactory keyFactory = KeyFactory.getInstance("RSA");
//		PublicKey pubKey = keyFactory.generatePublic(keySpec);
//		return ConvertionUtil.BytesToHexString(RSAEncrypt(pubKey, ConvertionUtil.StringToUTF8BytesPadding(rawData)),
//				true);
//	}

	public static byte[] RSAEncrypt(Key key, byte[] rawData) {
		try {
			Cipher cp = getRsaCipher();
			cp.init(Cipher.ENCRYPT_MODE, key);

			ByteArrayOutputStream buff = new ByteArrayOutputStream();
			int count = rawData.length / 117; // 一次只能加密117个字节11个字节为对齐
			int start = 0;
			for (int i = 0; i < count; i++) {
				buff.write(cp.doFinal(rawData, start, 117));
				start += 117;
			}

			if (start < rawData.length)
				buff.write(cp.doFinal(rawData, start, rawData.length - start));

			return buff.toByteArray();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	// RSADecrypt
	public static byte[] rsaDecryptByBase64(String base64) {
		return RSADecrypt(Base64.decodeBase64(base64.getBytes()));
	}

	public static String RSADecrypt(String rawData) {
		return RSADecrypt(rawData, Charset.defaultCharset());
	}

	public static String RSADecrypt(String rawData, Charset charset) {
		return new String(RSADecrypt(ConvertionUtil.HexStringToBytes(rawData)), charset).trim();
	}

	public static byte[] RSADecrypt(byte[] rawData) {
		return RSADecrypt(sDefPrivateKey, rawData);
	}

	public static byte[] RSADecrypt(Key key, byte[] ripeData) {

		try {
			Cipher cp = getRsaCipher();
			cp.init(Cipher.DECRYPT_MODE, key);

			ByteArrayOutputStream buff = new ByteArrayOutputStream();

			int count = ripeData.length / 128; // 解密按128个字节来解
			int start = 0;
			for (int i = 0; i < count; i++) {
				buff.write(cp.doFinal(ripeData, start, 128));
				start += 128;
			}

			if (start < ripeData.length)
				buff.write(cp.doFinal(ripeData, start, ripeData.length - start));

			return buff.toByteArray();

			// return cp.doFinal(rawData);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return null;
	}

	// MD5加密
	public static String MD5Encrypt(byte[] rawData) {
		try {
			MessageDigest md5 = MessageDigest.getInstance("MD5");
			md5.update(rawData);
			byte[] digestBytes = md5.digest();

			String md5Digest = ConvertionUtil.BytesToHexString(digestBytes, true); // encodeHexString(digestBytes);
			return md5Digest;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	public static String MD5Encrypt(String rawData) {
		return MD5Encrypt(rawData.getBytes(DEFAULT_CHARSET));
	}

//	/* AES start */
//	public static String aesEncrypt(String base64Key, String rawData) throws NoSuchAlgorithmException,
//			NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
//		return aesEncrypt(base64Key, rawData, DEFAULT_CHARSET);
//	}

//	public static String aesEncrypt(String base64Key, String rawData, Charset charset) throws NoSuchAlgorithmException,
//			NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
//		return new String(Base64.encodeBase64(aesEncrypt(base64Key, rawData.getBytes(charset))), charset);
//	}

//	public static byte[] aesEncrypt(String base64Key, byte[] rawData) throws NoSuchAlgorithmException,
//			NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
//		return aesEncrypt(convertBase64ToKeyForAES(base64Key), rawData);
//	}

	public static byte[] aesEncrypt(Key key, byte[] rawData) throws NoSuchAlgorithmException, NoSuchPaddingException,
			InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
		Cipher cp = getAesCipher();
		cp.init(Cipher.ENCRYPT_MODE, key);
		return cp.doFinal(rawData);
	}

//	public static String aesDecrypt(String base64Key, String base64Data) throws NoSuchAlgorithmException,
//			NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
//		return aesDecrypt(base64Key, base64Data, DEFAULT_CHARSET);
//	}

//	public static String aesDecrypt(String base64Key, String base64Data, Charset charset)
//			throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException,
//			BadPaddingException {
//		return new String(aesDecrypt(base64Key, Base64.decodeBase64(base64Data.getBytes(charset))), charset);
//	}

//	public static byte[] aesDecrypt(String base64Key, byte[] rawData) throws NoSuchAlgorithmException,
//			NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
//		return aesDecrypt(convertBase64ToKeyForAES(base64Key), rawData);
//	}

	public static byte[] aesDecrypt(Key key, byte[] rawData) throws NoSuchAlgorithmException, NoSuchPaddingException,
			InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
		Cipher cp = getAesCipher();
		cp.init(Cipher.DECRYPT_MODE, key);
		return cp.doFinal(rawData);
	}

//	public static Key convertBase64ToKeyForAES(String base64Key) {
//		return convertBase64ToKey(base64Key, "AES");
//	}

	public static String genAESKeyBase64Str() throws NoSuchAlgorithmException {
		return new String(Base64.encodeBase64(genAESKey()));
	}

	public static byte[] genAESKey() throws NoSuchAlgorithmException {
		return KeyGenerator.getInstance("AES").generateKey().getEncoded();
	}

	public static byte[] genAESKey(int keysize) throws NoSuchAlgorithmException {
		KeyGenerator kg = KeyGenerator.getInstance("AES");
		kg.init(keysize);
		return kg.generateKey().getEncoded();
	}
	/* AES end */

	/* DES start */
	public static String genDESKeyStr() {
		return ConvertionUtil.BytesToHexString(genDESKey(), true);
	}

	public static byte[] genDESKey() {
		try {
			return KeyGenerator.getInstance("DES").generateKey().getEncoded();
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return null;
	}

	public static String DESEncrypt(String key, String rawData) throws Exception {
		return ConvertionUtil.BytesToHexString(
				DESEncrypt(ConvertionUtil.HexStringToBytes(key), ConvertionUtil.StringToUTF8BytesPadding(rawData)),
				true);
	}

	public static byte[] DESEncrypt(byte[] key, byte[] rawData) throws Exception {

		SecretKey desKey = new SecretKeySpec(key, "DES");
		Cipher cp = Cipher.getInstance("DES/ECB/NoPadding");
		cp.init(Cipher.ENCRYPT_MODE, desKey);
		return cp.doFinal(rawData);
	}

	public static String DESDecrypt(String key, String rawData) {
		return DESDecrypt(key, rawData, Charset.defaultCharset());
	}

	public static String DESDecrypt(String key, String rawData, Charset charset) {
		return new String(DESDecrypt(ConvertionUtil.HexStringToBytes(key), ConvertionUtil.HexStringToBytes(rawData)),
				charset).trim();
	}

	public static byte[] DESDecrypt(byte[] key, byte[] ripeData) {
		try {
			SecretKey desKey = new SecretKeySpec(key, "DES");
			Cipher cp = Cipher.getInstance("DES/ECB/NoPadding");
			cp.init(Cipher.DECRYPT_MODE, desKey);
			return cp.doFinal(ripeData);
		} catch (Exception e) {
			e.printStackTrace();
		}

		return null;
	}
	/* DES end */

//	public static Key convertBase64ToKey(String base64Key, String algorithm) {
//		return new SecretKeySpec(Base64.decodeBase64(base64Key), algorithm);
//	}

}
